"""Indicator planning calculator."""
from dataclasses import dataclass, field
from typing import List, Optional

from .rule_parameters import RuleParameters


@dataclass
class Calculator(object):
    """
    Indicator planning calculator.

    Attributes:
        lower_rule_parameters (RuleParameters): parameters for the lower rule
        upper_rule_parameters (RuleParameters): parameters for the upper rule
        total_loss (float):
            the sum of all losses observed when expected classes are known and
            differ from those assigned by the rules
    """

    lower_rule_parameters: RuleParameters = field(default_factory=RuleParameters)
    upper_rule_parameters: RuleParameters = field(default_factory=RuleParameters)
    total_loss: float = 0.0

    def __post_init__(self):
        """Initialize class numbers for the rules."""
        self.lower_rule_parameters.class_numbers = [1, 2]
        self.upper_rule_parameters.class_numbers = [4, 3]

    def calculate(
        self,
        planned: List[float],
        actual: List[float],
        expected: Optional[List[int]] = None,
    ) -> List[int]:
        """
        Calculate indicator deviation classes.

        Arguments:
            planned (List[float]): the planned values of the indicator
            actual (List[float]): the actual observed values of the indicator
            expected (Optional[List[int]]): the expected classes
        Returns:
            List[float]: the assigned classes
        """
        result = []

        for t, (x_p, x_a, exp_class) in enumerate(
            zip(planned, actual, expected or actual)
        ):
            x_t = x_a - x_p
            if x_t < 0:
                parameters = self.lower_rule_parameters
            else:
                parameters = self.upper_rule_parameters
            classes = parameters.class_numbers
            class_t = classes[x_t > parameters.z[t]]
            result.append(class_t)
            loss = 0
            if class_t > exp_class:
                loss = x_t - parameters.a * parameters.e[-1]
            if class_t < exp_class:
                loss = parameters.b * (parameters.e[-1] - x_t)
            self.total_loss += abs(loss)
            # print(t, x_t, class_t, exp_class, loss)

            self.lower_rule_parameters.evaluate(x_t, result)
            self.upper_rule_parameters.evaluate(x_t, result)

        return result


if __name__ == "__main__":
    c = Calculator()
    print(
        c.calculate(
            [10, 12, 14, 16, 18, 20, 22, 24, 26],
            [9, 10, 11, 12, 13, 14, 15, 16, 17],
            [2, 2, 2, 2, 2, 1, 1, 1, 1],
        ),
        c.total_loss,
    )
